using System;
using System.Collections;
using System.IO;
using System.Web;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Search;
using SearchTools.Exceptions;
using SearchTools.Logging;
using SearchTools.QueryParser;

namespace SearchTools.Search
{
    /// <summary>
    /// Holder class for search methods
    /// </summary>
    public sealed class Searcher
    {
        /// <summary>
        /// Load index from default umbraco path
        /// </summary>
        /// <returns>Index reader</returns>
        private static IndexReader GetIndexReader()
        {
            IndexReader reader = null;

            if (HttpContext.Current != null)
            {
                reader =
                    IndexReader.Open(System.Web.HttpContext.Current.Server.MapPath(umbraco.GlobalSettings.StorageDirectory + "/umbSearch"));
            }
            else
            {
                //Assume index in bin/{Config}/index
                reader = IndexReader.Open("./index");
            }

            return reader;
        }

        /// <summary>
        /// Executes the specfied query against the default index, returning up to 10 results
        /// </summary>
        /// <param name="querystring">The querystring.</param>
        /// <returns>List of documents matching query.</returns>
        public static ResultCollection Search(string querystring)
        {
            return Search(querystring, GetIndexReader(), 10);
        }

        /// <summary>
        /// Executes the specfied query, returning up to max results
        /// </summary>
        /// <param name="querystring">The querystring.</param>
        /// <param name="max">The maximum number of results</param>
        /// <returns>List of documents matching query.</returns>
        public static ResultCollection Search(string querystring, int max)
        {
            return Search(querystring, GetIndexReader(), max);
        }

        /// <summary>
        /// Perform enchanced query against configured index
        /// </summary>
        /// <param name="querystring">The query string</param>
        /// <param name="index">The index.</param>
        /// <param name="max">The maximum number of results</param>
        /// <returns>List of documents matching query.</returns>
        private static ResultCollection Search(string querystring, IndexReader index, int max)
        {
            Logger.Write("Trace", string.Format("Executing \"{0}\"", querystring));

            StringReader inStr = new StringReader(querystring);
            QueryLexer lexer = new QueryLexer(inStr);
            QueryParser.QueryParser parser = new QueryParser.QueryParser(lexer);

            Query query = null;

            try
            {
                parser.query();

                QueryLanguageTreeParser treewalker = new QueryLanguageTreeParser();
                query = treewalker.query(parser.getAST());
            }
            catch (Exception e)
            {
                throw new SearchToolsException("Parser Error", e);
            }

            ResultCollection results;

            try
            {
                IndexSearcher searcher = new IndexSearcher(index);

                Hits hits = searcher.Search(query);

                results = new ResultCollection(searcher);

                Logger.Write("Trace", "Got " + hits.Length() + " results with " + query);

                for (int i = 0; i < hits.Length() & i < max; i++)
                {
                    Result result = new Result();

                    Document doc = hits.Doc(i);
                    result.Score = hits.Score(i);

                    IEnumerator fieldEnum = doc.Fields().GetEnumerator();

                    while (fieldEnum.MoveNext())
                    {
                        result[((Field) fieldEnum.Current).Name()] =
                            ((Field) fieldEnum.Current).StringValue();
                    }

                    results.Add(result);
                }
            }
            catch (Exception e)
            {
                throw new SearchToolsException("Error executing query", e);
            }
            finally
            {
                index.Close();
            }

            return results;
        }
    }
}